package com.tpshion.cloud.gateway.filter;

import com.tpshion.cloud.common.support.*;
import com.tpshion.cloud.common.utils.JsonUtil;
import com.tpshion.cloud.common.utils.JwtUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.concurrent.TimeUnit;

/**
 * token校验过滤器
 */
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {

    private static final Logger log = LoggerFactory.getLogger(AuthorizeFilter.class);

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        String token = request.getHeaders().getFirst("token");
        // token不为空需要校验token是否合法
        if(!StringUtils.isEmpty(token)){
            try {
                JwtUtil.checkToken(token);
                AuthInfo authInfo = JwtUtil.getUserInfo(token);
                log.info("*** authInfo:{}",authInfo);
                String key = Field.TOKEN + "-" + authInfo.getUserId();
                HashOperations<String,String,String> hashOperations = redisTemplate.opsForHash();
                String redisToken = hashOperations.get(key, Field.TOKEN);
                log.info("*** redisToken:{}",redisToken);
                if(StringUtils.isEmpty(redisToken)){
                    //token不一致，登录超时
                    return getVoidMono(response, ErrorCode.LOGIN_TIMEOUT);
                }
                if(!redisToken.equals(token)){
                    return getVoidMono(response, ErrorCode.TOKEN_ERROR);
                }
                Long expire = hashOperations.getOperations().getExpire(key, TimeUnit.MINUTES);
                if(expire < 0){
                    return getVoidMono(response, ErrorCode.LOGIN_TIMEOUT);
                }
                //判断toekn是否需要续期
                if(expire <= 2 && expire >= 0){
                    //重置token过期时间
                    redisTemplate.expire(key, Constants.TOKEN_EXPIRE_TIME, TimeUnit.MINUTES);
                    log.info("*** refresh token success");
                }
            }catch (CoreException e){
                log.info("ip:{} token校验失败,token={}",request.getRemoteAddress().getAddress().getHostAddress(),token);
                // token校验失败
                return getVoidMono(response, ErrorCode.TOKEN_ERROR);
            }
        }
        // 访问次数统计
        requestTotal();
        return chain.filter(exchange);
    }

    private Mono<Void> getVoidMono(ServerHttpResponse response, ErrorCode errorCode){
        response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
        DataBuffer dataBuffer = response.bufferFactory().wrap(JsonUtil.obj2String(Result.build(errorCode)).getBytes());
        return response.writeWith(Flux.just(dataBuffer));
    }

    @Override
    public int getOrder() {
        return -100;
    }

    /**
     * 访问次数统计
     */
    private void requestTotal(){
        ValueOperations valueOperations = redisTemplate.opsForValue();
        Boolean absent = valueOperations.setIfAbsent(Field.REQUEST_NUM_KEY, 1, 30, TimeUnit.DAYS);
        if(!absent){
            valueOperations.increment(Field.REQUEST_NUM_KEY);
        }
    }
}
